package edu.towson.cosc.cosc455.jsmole1.markdowncompiler;

import edu.towson.cosc.cosc455.interfaces.SyntaxAnalyzer;

/** @author jon smoley :D
 * 
 *  All methods in this class are alphabetized
 *  
 *  NOTE: the item() was renamed to listItems() to better fit Dr. Dehlinger's grammar on blackboard.
 *  NOTE: the "variable-define" rule was renamed to define because lazy.
 *  
 *  */
public class SuperHappyFuntimeSyntaxAnalyzer implements SyntaxAnalyzer {

	private SuperHappyFuntimeLexicalAnalyzer lexer;
	
	// Constructor used to initialize the lexer object
	public SuperHappyFuntimeSyntaxAnalyzer() {
		lexer = new SuperHappyFuntimeLexicalAnalyzer();
	}

	// <audio> ::=  #START AUDIO <text> #END AUDIO | empty
	/* (non-Javadoc)
	 * @see edu.towson.cosc.cosc455.interfaces.SyntaxAnalyzer#audio()
	 */
	@Override
	public void audio() throws CompilerException {
		
		if (SuperHappyFuntimeCompiler.debugStatements)
			System.out.println("\naudio()");
		
		if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.STARTAUDIO)) {
			
			// Add to parse tree, and get next token
			SuperHappyFuntimeCompiler.parseTree.add(SuperHappyFuntimeLexicalAnalyzer.token);
			lexer.getNextToken();
			
			text();
			
			if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.ENDAUDIO)) {
				
				// Add to parse tree, and get next token
				SuperHappyFuntimeCompiler.parseTree.add(SuperHappyFuntimeLexicalAnalyzer.token);
				lexer.getNextToken();
			} else {
				String message = "An #END AUDIO annotation was expected when " + SuperHappyFuntimeLexicalAnalyzer.token + " was found";
				throw new CompilerException(message);
			}
		} // Audio can be empty

	}
	
	// <body> ::=   <text> <body>
	//				| <paragraph> <body>
	//				| <bold> <body>
	//				| <italics> <body>
	//				| <list> <body>
	//				| empty
	// Method representing the document after the head
	// "Body" has no tags, so we do not call the parse tree or getNextToken()
	public void body() throws CompilerException {
		
		if (SuperHappyFuntimeCompiler.debugStatements)
			System.out.println("\nbody()");
		
		// If there are no # signs, then we know its valid text
		if (!(SuperHappyFuntimeLexicalAnalyzer.token.contains("#"))) {
			if (SuperHappyFuntimeCompiler.debugStatements)
				System.out.println("body - 1");
			text();
			body();
		} else if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.STARTPAR)) {
			if (SuperHappyFuntimeCompiler.debugStatements)
				System.out.println("body - 2");
			paragraph();
			body();
		} else if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.STARTBOLD)) {
			if (SuperHappyFuntimeCompiler.debugStatements)
				System.out.println("body - 3");
			bold();
			body();
		} else if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.STARTITALICS)) {
			if (SuperHappyFuntimeCompiler.debugStatements)
				System.out.println("body - 4");
			italics();
			body();
		} else if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.STARTLIST)) {
			if (SuperHappyFuntimeCompiler.debugStatements)
				System.out.println("body - 5");
			list();
			body();
		}
	}

	// <body> ::=  #START BOLD <define> <inner-text> #END BOLD
	/* (non-Javadoc)
	 * @see edu.towson.cosc.cosc455.interfaces.SyntaxAnalyzer#bold()
	 */
	@Override
	public void bold() throws CompilerException {
		
		if (SuperHappyFuntimeCompiler.debugStatements)
			System.out.println("\nbold()");
		
		if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.STARTBOLD)) {
			
			// Add to parse tree, and get next token
			SuperHappyFuntimeCompiler.parseTree.add(SuperHappyFuntimeLexicalAnalyzer.token);
			lexer.getNextToken();
			
			define();
			innerText();
			
			if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.ENDBOLD)) {
				
				// Add to parse tree, and get next token
				SuperHappyFuntimeCompiler.parseTree.add(SuperHappyFuntimeLexicalAnalyzer.token);
				lexer.getNextToken();
				
			} else {
				String message = "An #END BOLD annotation was expected when " + SuperHappyFuntimeLexicalAnalyzer.token + " was found";
				throw new CompilerException(message);
			}
			
		} // Bold isn't requied, so don't whine when we don't have one

	}

	// <define> ::=  #START DEFINE #NAME <text> #VALUE <text> #END DEFINE <DEFINE>
	//				 | empty
	/* (non-Javadoc)
	 * @see edu.towson.cosc.cosc455.interfaces.SyntaxAnalyzer#define()
	 */
	@Override
	public void define() throws CompilerException {
		
		if (SuperHappyFuntimeCompiler.debugStatements)
			System.out.println("\ndefine()");
		
		if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.STARTDEFINE)) {
			
			// Add to parse tree, and get next token
			SuperHappyFuntimeCompiler.parseTree.add(SuperHappyFuntimeLexicalAnalyzer.token);
			lexer.getNextToken();
			
			if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.NAME)) {
				
				// Add to parse tree, and get next token
				SuperHappyFuntimeCompiler.parseTree.add(SuperHappyFuntimeLexicalAnalyzer.token);
				lexer.getNextToken();
				
				text();
				
				if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.VALUE)) {
					
					// Add to parse tree, and get next token
					SuperHappyFuntimeCompiler.parseTree.add(SuperHappyFuntimeLexicalAnalyzer.token);
					lexer.getNextToken();
					
					text();
					
					if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.ENDDEFINE)) {
						
						// Add to parse tree, and get next token
						SuperHappyFuntimeCompiler.parseTree.add(SuperHappyFuntimeLexicalAnalyzer.token);
						lexer.getNextToken();
						
						define();
					} else { // End tags are required
						String message = "An #END DEFINE annotation was expected when " + SuperHappyFuntimeLexicalAnalyzer.token + " was found";
						throw new CompilerException(message);
					}
				} else { // Values are required
					String message = "A #VALUE annotation was expected when " + SuperHappyFuntimeLexicalAnalyzer.token + " was found";
					throw new CompilerException(message);
				}
			} else { // Names are required
				String message = "A #NAME annotation was expected when " + SuperHappyFuntimeLexicalAnalyzer.token + " was found";
				throw new CompilerException(message);
			}
			
		} // Variables aren't required

	}

	// <document> ::=  #START DOCUMENT <define> <head> <body> #END DOCUMENT
	/* (non-Javadoc)
	 * @see edu.towson.cosc.cosc455.interfaces.SyntaxAnalyzer#document()
	 */
	@Override
	public void document() throws CompilerException {
		
		if (SuperHappyFuntimeCompiler.debugStatements)
			System.out.println("\ndocument()");
		
		if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.STARTDOC)) {
			
			// Add to parse tree, and get next token
			SuperHappyFuntimeCompiler.parseTree.add(SuperHappyFuntimeLexicalAnalyzer.token);
			lexer.getNextToken();
			
			define();
			head();
			body();
			
			if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.ENDDOC)) {
				
				// Add to parse tree, and get next token
				SuperHappyFuntimeCompiler.parseTree.add(SuperHappyFuntimeLexicalAnalyzer.token);
//				lexer.getNextToken();
				
				// Since this is the end of the document, we need to see if there is more text.
				if (!(SuperHappyFuntimeLexicalAnalyzer.token.equals(null))) {
					//TODO Dr. Dehlinger told us to create a check at the end of a document for extra space
					// but also tells us to expect a space delimiter for the end of the file.
					
//					String message = "No text is allowed beyond the #END DOCUMENT annotation";
//					throw new CompilerException(message);
				}
				
			} else { // Since we have a start tag, we require an end tag
				String message = "An #END DOCUMENT annotation was expected when a " + SuperHappyFuntimeLexicalAnalyzer.token + " was found";
				throw new CompilerException(message);
			}
			
		} else if (!(SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.STARTDOC))) { // Document tags are required!
			if (SuperHappyFuntimeCompiler.debugStatements)
				System.out.println("not start document tag");
			
			String message = "A #START DOCUMENT annotaion was expected when a " + SuperHappyFuntimeLexicalAnalyzer.token + " was found";
			throw new CompilerException(message);
		}
		
	}
	
	// <head> ::=  #START HEAD <title> #END HEAD | empty
	/* (non-Javadoc)
	 * @see edu.towson.cosc.cosc455.interfaces.SyntaxAnalyzer#head()
	 */
	@Override
	public void head() throws CompilerException {
		
		if (SuperHappyFuntimeCompiler.debugStatements)
			System.out.println("\nhead()");
		
		if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.STARTHEAD)) {
			
			// Add to parse tree, and get next token
			SuperHappyFuntimeCompiler.parseTree.add(SuperHappyFuntimeLexicalAnalyzer.token);
			lexer.getNextToken();
			
			title();
			
			if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.ENDHEAD)) {
				
				// Add to parse tree, and get next token
				SuperHappyFuntimeCompiler.parseTree.add(SuperHappyFuntimeLexicalAnalyzer.token);
				lexer.getNextToken();
				
			} else {
				String message = "An #END HEAD annotation was expected when a " + SuperHappyFuntimeLexicalAnalyzer.token + " was found";
				throw new CompilerException(message);
			}
		} // Title tag not required, so don't throw an error if we don't find one
		
	}
	
	// <inner-list> ::=  <bold> <inner-list>
	//					 | <italics> <inner-list>
	//					 | <list> <inner-list>
	//					 | <inner-text> <inner-list>
	//					 | empty
	public void innerList() throws CompilerException {
		
		if (SuperHappyFuntimeCompiler.debugStatements)
			System.out.println("\ninner list()");
		
		if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.STARTBOLD)) {			
			bold();
			innerList();		
		} else if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.STARTITALICS)) {			
			italics();
			innerList();			
		} else if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.STARTLIST)) {			
			list();
			innerList();
		} else if (!(SuperHappyFuntimeLexicalAnalyzer.token.contains("#"))) {
			 innerText();
			 innerList();
		 } // Because use is optional, throw no error if start tag isn't found
		
	}	
	
	// <inner-paragraph> ::=  <text> <inner-paragraph>
	//						  | <bold> <inner-paragraph>
	//						  | <italics> <inner-paragraph>
	//						  | <list> <inner-paragraph>
	//						  | <newline> <inner-paragraph>
	//						  | empty
	public void innerParagraph() throws CompilerException {
		
		if (SuperHappyFuntimeCompiler.debugStatements)
			System.out.println("\ninner paragraph()");
		
		// If there are no # signs, then we know its valid text
		if (!(SuperHappyFuntimeLexicalAnalyzer.token.contains("#"))) {
			text();
			body();
		} else  if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.STARTBOLD)) {
			bold();
			innerParagraph();
		} else if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.STARTITALICS)) {
			italics();
			innerParagraph();
		} else if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.STARTLIST)) {
			list();
			innerParagraph();
		} else if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.NEWLINE)) {
			
			// Add to parse tree, and get next token
			SuperHappyFuntimeCompiler.parseTree.add(SuperHappyFuntimeLexicalAnalyzer.token);
			lexer.getNextToken();
			
			innerParagraph();
		}		
		
	}
	
	// <inner-text> ::=  <use> <inner-text>
	//					 | <text> <inner-text>
	//					 | <newline> <inner-text>
	//					 | empty
	public void innerText() throws CompilerException {
		
		if (SuperHappyFuntimeCompiler.debugStatements)
			System.out.println("\ninner text()");
		
		// If there are no # signs, then we know its valid text
		if (!(SuperHappyFuntimeLexicalAnalyzer.token.contains("#"))) {
			text();
			innerText();
		} else if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.STARTUSE)) {
			use();
			innerText();
		} else if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.NEWLINE)) {
			
			// Add to parse tree, and get next token
			SuperHappyFuntimeCompiler.parseTree.add(SuperHappyFuntimeLexicalAnalyzer.token);
			lexer.getNextToken();
			
			innerText();
		} // inner text is optional, so we don't require tags
		
	}

	// <italics> ::=  #START ITALICS <define> <inner-text> #END ITALICS
	/* (non-Javadoc)
	 * @see edu.towson.cosc.cosc455.interfaces.SyntaxAnalyzer#italics()
	 */
	@Override
	public void italics() throws CompilerException {
		
		if (SuperHappyFuntimeCompiler.debugStatements)
			System.out.println("\nitalics()");
		
		if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.STARTITALICS)) {
			
			// Add to parse tree, and get next token
			SuperHappyFuntimeCompiler.parseTree.add(SuperHappyFuntimeLexicalAnalyzer.token);
			lexer.getNextToken();
			
			define();
			innerText();
			
			if (!(SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.ENDITALICS))) {
				String message = "An #END ITALICS annotation was expected when a " + SuperHappyFuntimeLexicalAnalyzer.token + " was found";
				throw new CompilerException(message);
			}
		} // Italics aren't required
		
	}

	// <list> ::=  #START LIST #START ITEM <define> <inner-list> #END ITEM <list-items> #END LIST
	/* (non-Javadoc)
	 * @see edu.towson.cosc.cosc455.interfaces.SyntaxAnalyzer#list()
	 */
	@Override
	public void list() throws CompilerException {
		
		if (SuperHappyFuntimeCompiler.debugStatements)
			System.out.println("\nlist()");
		
		if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.STARTLIST)) {
			
			// Add to parse tree, and get next token
			SuperHappyFuntimeCompiler.parseTree.add(SuperHappyFuntimeLexicalAnalyzer.token);
			lexer.getNextToken();
			
			if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.STARTITEM)) {
				
				// Add to parse tree, and get next token
				SuperHappyFuntimeCompiler.parseTree.add(SuperHappyFuntimeLexicalAnalyzer.token);
				lexer.getNextToken();
				
				define();
				innerList();
				
				if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.ENDITEM)) {
					
					// Add to parse tree, and get next token
					SuperHappyFuntimeCompiler.parseTree.add(SuperHappyFuntimeLexicalAnalyzer.token);
					lexer.getNextToken();
					
					listItems();
					
					if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.ENDLIST)) {
						
						// Add to parse tree, and get next token
						SuperHappyFuntimeCompiler.parseTree.add(SuperHappyFuntimeLexicalAnalyzer.token);
						lexer.getNextToken();
						
					} else {
						String message = "An #END LIST annotation was expected when a " + SuperHappyFuntimeLexicalAnalyzer.token + " was found";
						throw new CompilerException(message);
					}
					
				} else { // #END ITEM tag required
					String message = "An #END ITEM annotation was expected when a " + SuperHappyFuntimeLexicalAnalyzer.token + " was found";
					throw new CompilerException(message);
				}
			}
		}

	}

	// <list-items> ::=  #START ITEM <define> <inner-list> #END ITEM <list-items> | empty
	/* (non-Javadoc)
	 * @see edu.towson.cosc.cosc455.interfaces.SyntaxAnalyzer#item()
	 */
	@Override
	public void listItems() throws CompilerException {
		
		if (SuperHappyFuntimeCompiler.debugStatements)
			System.out.println("\nlist items()");
		
		if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.STARTITEM)) {
			
			// Add to parse tree, and get next token
			SuperHappyFuntimeCompiler.parseTree.add(SuperHappyFuntimeLexicalAnalyzer.token);
			lexer.getNextToken();
			
			define();
			innerList();
			
			if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.ENDITEM)) {
				
				// Add to parse tree, and get next token
				SuperHappyFuntimeCompiler.parseTree.add(SuperHappyFuntimeLexicalAnalyzer.token);
				lexer.getNextToken();
				
				listItems();
				
			} else {
				String message = "An #END ITEM annotation was expected when a " + SuperHappyFuntimeLexicalAnalyzer.token + " was found";
				throw new CompilerException(message);
			}
		} // list-items can be empty, so throw no exception

	}
	
	// <paragraph> ::=  #START PARAGRAPH <define> <inner-paragraph> #END PARAGRAPH
	/* (non-Javadoc)
	 * @see edu.towson.cosc.cosc455.interfaces.SyntaxAnalyzer#paragraph()
	 */
	@Override
	public void paragraph() throws CompilerException {
		
		if (SuperHappyFuntimeCompiler.debugStatements)
			System.out.println("\nparagraph()");
		
		if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.STARTPAR)) {
			
			// Add to parse tree, and get next token
			SuperHappyFuntimeCompiler.parseTree.add(SuperHappyFuntimeLexicalAnalyzer.token);
			lexer.getNextToken();
			
			define();
			innerParagraph();
			
			if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.ENDPAR)) {
				
				// Add to parse tree, and get next token
				SuperHappyFuntimeCompiler.parseTree.add(SuperHappyFuntimeLexicalAnalyzer.token);
				lexer.getNextToken();
				
			} else { // Since we have a start tag, we require an end tag
				String message = "An #END PARAGRAPH annotation was expected when a " + SuperHappyFuntimeLexicalAnalyzer.token + " was found";
				throw new CompilerException(message);
			}
			
		} // Since paragraph tags are optional, we don't require an end tag
		
	}

	// <text> ::=  text | empty
	// This method was added to the original interface
	public void text() throws CompilerException {
		
		if (SuperHappyFuntimeCompiler.debugStatements)
			System.out.println("\ntext()");
		
		// If there is text that isn't a possible token
		if (!(SuperHappyFuntimeLexicalAnalyzer.token.contains("#"))) {
			
			// Then we have text. Add to parse tree
			SuperHappyFuntimeCompiler.parseTree.add(SuperHappyFuntimeLexicalAnalyzer.token);
			lexer.getNextToken();
		}
	}
	
	// <title> ::=  #START TITLE <text> #END TITLE | empty
	/* (non-Javadoc)
	 * @see edu.towson.cosc.cosc455.interfaces.SyntaxAnalyzer#title()
	 */
	@Override
	public void title() throws CompilerException {
		
		if (SuperHappyFuntimeCompiler.debugStatements)
			System.out.println("\ntitle()");
		
		if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.STARTTITLE)) {
			
			// Add to parse tree, and get next token
			SuperHappyFuntimeCompiler.parseTree.add(SuperHappyFuntimeLexicalAnalyzer.token);
			lexer.getNextToken();
			
			text();
			
			if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.ENDTITLE)) {
				
				// Add to parse tree, and get next token
				SuperHappyFuntimeCompiler.parseTree.add(SuperHappyFuntimeLexicalAnalyzer.token);
				lexer.getNextToken();
				
			} else { // Since we have a start tag, we require an end tag
				String message = "A #Start title annotation was expected when a " + SuperHappyFuntimeLexicalAnalyzer.token + " was found";
				throw new CompilerException(message);
			}
		} // Title tags are optional, so don't compliain when we don't find one

	}

	// <use> ::=  #START USE <text> #END USE | empty
	/* (non-Javadoc)
	 * @see edu.towson.cosc.cosc455.interfaces.SyntaxAnalyzer#use()
	 */
	@Override
	public void use() throws CompilerException {
		
		if (SuperHappyFuntimeCompiler.debugStatements)
			System.out.println("\nuse()");
		
		if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.STARTUSE)) {
			
			// Add to parse tree, and get next token
			SuperHappyFuntimeCompiler.parseTree.add(SuperHappyFuntimeLexicalAnalyzer.token);
			lexer.getNextToken();
			
			text();
			
			if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.ENDUSE)) {
				
				// Add to parse tree, and get next token
				SuperHappyFuntimeCompiler.parseTree.add(SuperHappyFuntimeLexicalAnalyzer.token);
				lexer.getNextToken();
				
			} else { // Since we have a start tag, we require an end tag
				String message = "An END USE annotation was expected when a " + SuperHappyFuntimeLexicalAnalyzer.token + " was found";
				throw new CompilerException(message);
			}
			
		} // Because use is optional, throw no error if start tag isn't found

	}

	// <video> ::=  #START VIDEO <text> #END VIDEO | empty
	/* (non-Javadoc)
	 * @see edu.towson.cosc.cosc455.interfaces.SyntaxAnalyzer#video()
	 */
	@Override
	public void video() throws CompilerException {
		
		if (SuperHappyFuntimeCompiler.debugStatements)
			System.out.println("\nvideo()");
		
		if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.STARTVIDEO)) {
			
			// Add to parse tree, and get next token
			SuperHappyFuntimeCompiler.parseTree.add(SuperHappyFuntimeLexicalAnalyzer.token);
			lexer.getNextToken();
			
			text();
			
			if (SuperHappyFuntimeLexicalAnalyzer.token.equalsIgnoreCase(Tag.ENDVIDEO)) {
				
				// Add to parse tree, and get next token
				SuperHappyFuntimeCompiler.parseTree.add(SuperHappyFuntimeLexicalAnalyzer.token);
				lexer.getNextToken();
			} else {
				String message = "An #END VIDEO annotation was expected when " + SuperHappyFuntimeLexicalAnalyzer.token + " was found";
				throw new CompilerException(message);
			}
		} // Video can be empty

	}

	

}
